#include "qbciscreencontroller.h"
#include <QtDebug>
#include <QtMath>
#include <iostream>

QBciScreenController::QBciScreenController(QObject *parent) : QObject(parent) {
  for (int iB = 0; iB < BlockNum; ++iB) {
    BlockConfig config;
    config.ID = iB;
    ConfigList << config;
  }
}

QBciScreenController::~QBciScreenController() { resetAllBlock(); }

void QBciScreenController::setBlock(const int &ID, const bool &enable,
                                const double &freq, const double &duti,
                                const QString &label) {
  setBlockEnable(ID, enable);
  setBlockFreq(ID, freq);
  setBlockDuti(ID, duti);
  setBlockLabel(ID, label);
}

void QBciScreenController::setBlock(const int &ID, const bool &enable,
                                const double &freq, const double &duti) {
  setBlockEnable(ID, enable);
  setBlockFreq(ID, freq);
  setBlockDuti(ID, duti);
}

void QBciScreenController::setBlock(const int &ID, const bool &enable,
                                    const double &duti,const QString &label){
    setBlockEnable(ID, enable);
    setBlockDuti(ID, duti);
    setBlockLabel(ID, label);
}

void QBciScreenController::setBlockEnable(const int &ID, const bool &enable) {
  ConfigList[ID].Enable = enable;
}

void QBciScreenController::setBlockFreq(const int &ID, const double &freq) {
  ConfigList[ID].Freq = freq;
}

void QBciScreenController::setBlockDuti(const int &ID, const double &duti) {
  ConfigList[ID].Duti = duti;
}

void QBciScreenController::setBlockLabel(const int &ID, const QString &label) {
  ConfigList[ID].Label = label;
}

void QBciScreenController::setBlockFlick(const int &ID, const bool &flick) {
  ConfigList[ID].Flick = flick;
}

void QBciScreenController::setBlockEnable(const QString &label,
                                      const bool &enable) {
  for (auto config : ConfigList) {
    if (config.Label == label) {
      config.Enable = enable;
    }
  }
}

void QBciScreenController::setBlockFreq(const QString &label, const double &freq) {
  for (auto config : ConfigList) {
    if (config.Label == label) {
      config.Freq = freq;
    }
  }
}

void QBciScreenController::setBlockDuti(const QString &label, const double &duti) {
  for (auto config : ConfigList) {
    if (config.Label == label) {
      config.Duti = duti;
    }
  }
}

void QBciScreenController::setBlockFlick(const QString &label, const bool &flick) {
  for (auto config : ConfigList) {
    if (config.Label == label) {
      config.Flick = flick;
    }
  }
}

void QBciScreenController::setWaveType(const QBciRender::WaveType &wave) {
  Wave = wave;
}

void QBciScreenController::setAllBlock() {
//  setBlock(0, true, 14.3, 0.5, "stop");
//  setBlock(1, true, 21.3, 0.5, "whistle");
//  setBlock(2, true, 10, 0.5, "none");
//  setBlockFlick(2, false);
//  setBlock(3, true, 17.3, 0.5, "left");
//  setBlock(4, true, 10.3, 0.5, "none");
//  setBlockFlick(4, false);
//  setBlock(5, true, 15.3, 0.5, "front");
//  setBlock(6, true, 18.3, 0.5, "back");
//  setBlock(7, true, 10.3, 0.5, "none");
//  setBlockFlick(7, false);
//  setBlock(8, true, 16.3, 0.5, "right");
//  setBlock(9, true, 21.3, 0.5, "none");
//  setBlockFlick(9, false);
//  setBlock(10, true, 20.3, 0.5, "speed_up");
//  setBlock(11, true, 19.3, 0.5, "speed_down");
//  setBlock(12, true, 10.3, 0.5, "none");
//  setBlockFlick(12, false);

  setBlock(0, true, 0.5, "stop");
  setBlock(1, true, 0.5, "whistle");
  setBlock(2, true, 0.5, "none");
  setBlockFlick(2, false);
  setBlock(3, true, 0.5, "left");
  setBlock(4, true, 0.5, "none");
  setBlockFlick(4, false);
  setBlock(5, true, 0.5, "front");
  setBlock(6, true, 0.5, "back");
  setBlock(7, true, 0.5, "none");
  setBlockFlick(7, false);
  setBlock(8, true, 0.5, "right");
  setBlock(9, true, 0.5, "none");
  setBlockFlick(9, false);
  setBlock(10, true, 0.5, "speed_up");
  setBlock(11, true, 0.5, "speed_down");
  setBlock(12, true, 0.5, "none");
  setBlockFlick(12, false);

  getConstruction();
}

void QBciScreenController::resetAllBlock() {
  std::cout << "Reset All Block" << std::endl;
  QList<bool> save;
  for (int iB = 0; iB < BlockNum; ++iB) {
    save.append(ConfigList[iB].Flick);
    ConfigList[iB].Flick = false;
  }
  getConstruction();
  for (int iB = 0; iB < BlockNum; ++iB) {
    ConfigList[iB].Flick = save.at(iB);
  }
}

QByteArray QBciScreenController::getConstruction() {

  uint8_t header = 58;
  QByteArray construction;
  construction.append(header);

  auto f = [=](const uint16_t &u16, uint8_t &u8H, uint8_t &u8L) {
    uint16_t maskLow = 0xFF, maskHigh = 0xFF00;
    u8H = static_cast<uint8_t>((maskHigh & u16) >> 8);
    u8L = static_cast<uint8_t>(maskLow & u16);
  };

  for (int iB = 0; iB < BlockNum; ++iB) {
    const BlockConfig &config = ConfigList.at(iB);
    uint16_t freq = static_cast<uint16_t>(config.Freq * 100);
    uint8_t freqHigh;
    uint8_t freqLow;
    f(freq, freqHigh, freqLow);

    uint8_t ctlBit = 0;
    if (!config.Enable) {
      ctlBit += 8;
    }
    if (config.Flick) {
      ctlBit += 1;
    } else {
      ctlBit += 2;
    }

    uint16_t duti = static_cast<uint16_t>(config.Duti * 1000);
    uint8_t dutiHigh;
    uint8_t dutiLow;
    f(duti, dutiHigh, dutiLow);

    construction.append(freqHigh)
        .append(freqLow)
        .append(ctlBit)
        .append(dutiHigh)
        .append(dutiLow);
  }

  switch (Wave) {
  case QBciRender::square:
    construction.append(uint8_t(85));
    for (int iB = 0; iB < BlockNum; ++iB) {
      construction.append(char(0)).append(char(0));
    }
    break;
  case QBciRender::sin:
    construction.append(uint8_t(170));
    for (int iB = 0; iB < BlockNum; ++iB) {
      uint16_t freq = static_cast<uint16_t>(ConfigList.at(iB).Freq * 85.8993);
      uint8_t freqHigh;
      uint8_t freqLow;
      f(freq, freqHigh, freqLow);
      construction.append(freqHigh).append(freqLow);
    }
    break;
  }

  construction.append(uint8_t(59));

  emit newConstruction(construction);
  return construction;
}

void QBciScreenController::ScreenControllerInit(){
    screenControllerThread = new QThread();
    this->moveToThread(screenControllerThread);
    connect(screenControllerThread, &QThread::finished, this, &QObject::deleteLater);
}
